﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data.Entity;
using System.Data;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using LAVAA.DAL;

namespace LAVAA.DAL
{
    /// <summary>
    /// Är ansvarigt för att exponera de metoder som enbart har med dataaccess och Activity att göra
    /// </summary>
    public class ActivityRepository : GenericDataRepository<Activity>
    {
        public ActivityRepository(DbContext context)
            : base(context)
        {
            RegisterForValidation(typeof(ActivityValidation));
        }
     
        public Activity GetByActivityTypeId(Guid activityTypeId)
        {
            return _objectSet.SingleOrDefault(a => a.ActivityType.Id == activityTypeId);
        }

        public override void Add(Activity entity)
        {
            Validate(entity);

            if (entity.Id == Guid.Empty)
                entity.Id = Guid.NewGuid();
            _objectSet.Add(entity);
        }
    }

    /// <summary>
    /// Är ansvarigt för att exponera de metoder som enbart har med dataaccess och Activity att göra
    /// </summary>
    public class UserRepository : GenericDataRepository<User>
    {
        public UserRepository(DbContext context)
            : base(context)
        {
        }

        public override void Add(User entity)
        {
            if (entity.Id == Guid.Empty)
                entity.Id = Guid.NewGuid();
            _objectSet.Add(entity);
        }
    }

    /// <summary>
    /// Är ansvarigt för att exponera de metoder som enbart har med dataaccess och Activity att göra
    /// </summary>
    public class UserGroupRepository : GenericDataRepository<UserGroup>
    {
        public UserGroupRepository(DbContext context)
            : base(context)
        {
        }

        public override void Add(UserGroup entity)
        {
            if (entity.Id == Guid.Empty)
                entity.Id = Guid.NewGuid();
            _objectSet.Add(entity);
        }
    }

    /// <summary>
    /// Är ansvarigt för att exponera de metoder som enbart har med dataaccess och Activity att göra
    /// </summary>
    public class ActivityTypeRepository : GenericDataRepository<ActivityType>
    {
        public ActivityTypeRepository(DbContext context)
            : base(context)
        {
        }

        public override void Add(ActivityType entity)
        {
            if (entity.Id == Guid.Empty)
                entity.Id = Guid.NewGuid();
            _objectSet.Add(entity);
        }
    }

    public class RewardTypeRepository : GenericDataRepository<RewardType>
    {
        public RewardTypeRepository(DbContext context)
            : base(context)
        {
            RegisterForValidation(typeof(RewardTypeValidation));
        }

        public override void Add(RewardType entity)
        {
            Validate(entity);
            if (entity.Id == Guid.Empty)
                entity.Id = Guid.NewGuid();
            _objectSet.Add(entity);
        }
    }

    /// <summary>
    /// Ett generiskt datarepository med de mest vanliga metoderna för dataaccess
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class GenericDataRepository<T> where T : class
    {
        protected DbSet<T> _objectSet;
        private DbContext _context; 

        public GenericDataRepository(DbContext context)
        {
            _objectSet = context.Set<T>();
            _context = context;
        }

        public IEnumerable<T> GetAll()
        {
            return _objectSet;
        }

        public virtual T GetById(Guid id)
        {
            return _objectSet.Find(id);
        }

        /// <summary>
        /// Använder ett filter och/eller orderby för att returnera entiteter
        /// usage: Find(filter: a => a.ActivityType.Name.Contains(activityTypeName), orderBy: q => q.OrderBy(d => d.Date))
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        public IEnumerable<T> Find(
            Expression<Func<T, bool>> filter = null, 
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null)
        {
            IQueryable<T> query = _objectSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        public abstract void Add(T entity);
        

        public virtual void Delete(object id)
        {
            T entityToDelete = _objectSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete(T entityToDelete)
        {
            if (_context.Entry(entityToDelete).State == EntityState.Detached)
            {
                _objectSet.Attach(entityToDelete);
            }
            _objectSet.Remove(entityToDelete);
        }

        public virtual void Update(T entityToUpdate)
        {
            _objectSet.Attach(entityToUpdate);
            _context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        public static void RegisterForValidation(Type validationObjectType)
        {
            TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(T), validationObjectType), typeof(T));
        }

        public static void Validate(object entity)
        {
            ValidationContext context = new ValidationContext(entity, null, null);
            List<ValidationResult> validationResult = new List<ValidationResult>();
            bool isValid = Validator.TryValidateObject(entity, context, validationResult, true);
            if (!isValid)
            {
                throw new DAL.Exceptions.ValidationException("Ett valideringsfel uppstod", validationResult);
            }
        }
    }

    //public class DataRepository
    //{
    //    private LavaaEntitiesConnection _context;

    //    public DataRepository(LavaaEntitiesConnection context)
    //    {
    //        _context = context;
    //    }

    //    public User GetUserById(Guid id)
    //    {
    //        return _context.Users.Single(u => u.Id == id);
    //    }


    //    public Activity GetActivityById(Guid id)
    //    {
    //        return _context.Activities.Single(a => a.Id == id);
    //    }

    //    public ActivityType GetActivityTypeById(Guid id)
    //    {
    //        return _context.ActivityTypes.Single(a => a.Id == id);
    //    }

    //    public void Add(ActivityType type)
    //    {
    //        _context.ActivityTypes.Add(type);
    //    }



    //    public ICollection<Activity> Find(Expression<Func<Activity, bool>> filter)
    //    {
    //        return _context.Activities.Where(filter).ToList();
    //    }

    //    public void Add(Activity activity)
    //    {
    //        if (activity.Id == Guid.Empty)
    //            activity.Id = Guid.NewGuid();
    //        _context.Activities.Add(activity);
    //    }
    //    public void Delete(Activity activity)
    //    {
    //        _context.Activities.Remove(activity);
    //    }

    //    public void Update(Activity activity)
    //    {
    //        _context.Activities.Attach(activity);
    //    }

    //    public ICollection<Activity> GetAll()
    //    {
    //        throw new NotImplementedException();
    //    }
    //}
}
